package org.oimwrapper.connectors.adapters;

import com.thortech.util.logging.*;
import java.util.*;

import org.oimwrapper.connectors.*;

/**
 * Checks access policy flags and re-adds them as needed.
 */
public class OIMPolicyFlagChecker {
	
	/**
	 * A reference to the calling adapter.
	 */
	protected OIMAdapterBase oimAdapter;
	
	/**
	 * A reference to the calling task.
	 */
	protected OIMTaskBase oimTask;
	
	/**
	 * The logger being used.
	 */
	protected Logger logger;
	
	/**
	 * A map that holds the locks for all users.
	 */
	private Map userLocks;

	/**
	 * This constructor is to be used from adapters.
	 * 
	 * @param OimAdapter The calling adapter.
	 */
	public OIMPolicyFlagChecker(OIMAdapterBase OimAdapter) {
		oimAdapter = OimAdapter;
		oimTask = null;
		logger = OimAdapter.logger;
		userLocks = new HashMap();
	}
	
	/**
	 * This constructor is to be used from scheduled tasks.
	 * 
	 * @param OimTask The calling task.
	 */
	public OIMPolicyFlagChecker(OIMTaskBase OimTask) {
		oimAdapter = null;
		oimTask = OimTask;
		logger = OimTask.logger;
		userLocks = new HashMap();
	}
	
	/**
	 * Obtains a lock for a user.
	 * 
	 * @param UserKey The key of the user to get the lock for.
	 * @exception Exception
	 */
	private void lock(long UserKey) throws Exception {
		Long key = new Long(UserKey);
		while (true) {
			synchronized(userLocks) {
				Object result = userLocks.get(key);
				if (result == null) {
					userLocks.put(key, key);
					break;
				}
			}
			Thread.sleep(500);
		}
	}
	
	/**
	 * Releases a lock for a user.
	 * 
	 * @param UserKey The key of the user to release the lock for.
	 */
	private void unlock(long UserKey) {
		Long key = new Long(UserKey);
		synchronized(userLocks) {
			userLocks.remove(key);
		}
	}
	
	/**
	 * Wrapper to query from either the adapter on the task depending on how this object 
	 * was created.
	 * 
	 * @param Query The query to run.
	 * @return An array of Maps, one for each record returned.
	 * @exception Exception
	 */
	protected Map[] getOimData(String Query) throws Exception {
		logger.debug("Query = " + Query);
		Map[] result = null;
		if (oimAdapter != null) {
			result = oimAdapter.getOimData(Query);
		} else if (oimTask != null) {
			result = oimTask.getOimData(Query);
		}
		return result;
	}
	
	/**
	 * Wrapper to update from either the adapter on the task depending on how this object 
	 * was created.
	 * 
	 * @param Query The query to run.
	 * @exception Exception
	 */
	protected void executeOimQuery(String Query) throws Exception {
		logger.debug("Query = " + Query);
		if (oimAdapter != null) {
			oimAdapter.executeOimQuery(Query);
		} else if (oimTask != null) {
			oimTask.executeOimQuery(Query);
		}
	}
	
	/**
	 * Processes the fields in the child data for an object, and adds the policy key as needed.
	 * 
	 * @param PolicyKey The policy key to link in.
	 * @param ProcessInstanceKey The process instance key of the object to process.
	 * @param ObjectKey The object key of the object to process.
	 * @exception Exception
	 */
	protected void processChildFields(long PolicyKey, long ProcessInstanceKey, long ObjectKey) throws Exception {
		logger.debug("Entering policyFlagChecker.processChildFields()");
		Map[] childFields = getOimData("select sdk_name, poc_record_number from poc inner join sdk on poc.sdk_key = sdk.sdk_key where pol_key = " + Long.toString(PolicyKey) + " and poc.obj_key = " + Long.toString(ObjectKey));
		for (int i = 0; i < childFields.length; i++) {
			String table = (String) childFields[i].get("Structure Utility.Table Name");
			logger.debug("table = " + table);
			String query = "update " + table + " set pol_key = " + Long.toString(PolicyKey) + " where orc_key = " + Long.toString(ProcessInstanceKey);
			long recordNumber = Long.parseLong((String) childFields[i].get("POC_RECORD_NUMBER"));
			Map[] childFieldData = getOimData("select poc_field_name, poc_field_value from poc inner join sdk on poc.sdk_key = sdk.sdk_key where pol_key = " + Long.toString(PolicyKey) + " and poc.obj_key = " + Long.toString(ObjectKey) + " and sdk_name = '" + table + "' and poc_record_number = " + Long.toString(recordNumber));
			for (int j = 0; j < childFieldData.length; j++) {
				String field = (String) childFieldData[j].get("POC_FIELD_NAME");
				logger.debug("field = " + field);
				String value = (String) childFieldData[j].get("POC_FIELD_VALUE");
				logger.debug("value = " + value);
				query += " and " + field + " = '" + value + "'";
			}
			executeOimQuery(query);
		}
		logger.debug("Exiting policyFlagChecker.processChildFields()");
	}
	
	/**
	 * Gets the details for all resource instances.
	 * 
	 * @return The details for all resource instances.
	 * @exception Exception
	 */
	public Map[] getResourceDetails() throws Exception {
		return getOimData("select oiu_key, oiu.orc_key, oiu.pol_key, obi.obj_key, pop_revoke_object, oiu.usr_key from oiu inner join obi on oiu.obi_key = obi.obi_key left outer join pop on oiu.pol_key = pop.pol_key and obi.obj_key = pop.obj_key");
	}
	
	/**
	 * Gets the details for a specific resource instance.
	 * 
	 * @param ProcessInstanceKey The process instance key of the instance to get the details for.
	 * @return The details for a specific resource instance.
	 * @exception Exception
	 */
	public Map[] getResourceDetails(long ProcessInstanceKey) throws Exception {
		return getOimData("select oiu_key, oiu.orc_key, oiu.pol_key, obi.obj_key, pop_revoke_object, oiu.usr_key from oiu inner join obi on oiu.obi_key = obi.obi_key left outer join pop on oiu.pol_key = pop.pol_key and obi.obj_key = pop.obj_key where oiu.pol_key is not null and oiu.orc_key = " + Long.toString(ProcessInstanceKey));
	}
	
	/**
	 * Processes a single resource instance.  If invoking this method in a threaded environment
	 * you must invoke this object from a single, shared object instance.   The single object 
	 * instance will take care of any required locking amongst threads.
	 * 
	 * @param ResourceData The resource instance data of the record to process.
	 */
	public void processSingleInstance(Map ResourceData) {
		logger.debug("Entering policyFlagChecker.processSingleInstance()");
		try {
			long objectInstanceKey = Long.parseLong((String) ResourceData.get("Users-Object Instance For User.Key"));
			logger.debug("objectInstanceKey = " + Long.toString(objectInstanceKey));
			long processInstanceKey = Long.parseLong((String) ResourceData.get("Process Instance.Key"));
			logger.debug("processInstanceKey = " + Long.toString(processInstanceKey));
			long objectKey = Long.parseLong((String) ResourceData.get("Objects.Key"));
			logger.debug("objectKey = " + Long.toString(objectKey));
			long userKey = Long.parseLong((String) ResourceData.get("Users.Key"));
			logger.debug("userKey = " + Long.toString(userKey));
			long policyKey = 0L;
			long revokeFlag = 0L;
			try {
				policyKey = Long.parseLong((String) ResourceData.get("Access Policies.Key"));
				revokeFlag = Long.parseLong((String) ResourceData.get("Access Policy-Resource Objects.Revoke Objects"));
			} catch (NumberFormatException e) { ; }
			logger.debug("policyKey = " + Long.toString(policyKey));
			logger.debug("revokeFlag = " + Long.toString(revokeFlag));
			if (policyKey != 0L) {
				executeOimQuery("update oiu set oiu_policy_revoke = " + Long.toString(revokeFlag) + ", oiu_policy_based = 1 where oiu_key = " + Long.toString(objectInstanceKey));
			} else {
				lock(userKey);
				try {
					if (getOimData("select oiu.orc_key from oiu inner join obi on oiu.obi_key = obi.obi_key where pol_key is not null and oiu.usr_key = " + Long.toString(userKey) + " and obj_key = " + Long.toString(objectKey)).length == 0) {
						Map[] resources = getOimData("select pol.pol_key, pop_revoke_object from pop inner join pol on pop.pol_key = pol.pol_key inner join pog on pol.pol_key = pog.pol_key inner join usg on pog.ugp_key = usg.ugp_key where usr_key = " + Long.toString(userKey) + " and obj_key = " + Long.toString(objectKey) + " and pop_denial = 0 order by pol_priority desc");
						if (resources.length > 0) {
							policyKey = Long.parseLong((String) resources[0].get("Access Policies.Key"));
							revokeFlag = Long.parseLong((String) resources[0].get("Access Policy-Resource Objects.Revoke Objects"));
							long bestObjectInstanceKey = objectInstanceKey;
							Map[] data = getOimData("select sdk_name, pof_field_name, pof_field_value from pof inner join sdk on pof.sdk_key = sdk.sdk_key where pof_field_name in (select sdc_name from sdc inner join pof on sdc.sdk_key = pof.sdk_key where sdc_field_type = 'ITResourceLookupField' and pol_key = " + Long.toString(policyKey) + ") and pol_key = " + Long.toString(policyKey));
							if (data.length > 0) {
								String table = (String) data[0].get("Structure Utility.Table Name");
								String query = "select oiu_key from oiu inner join " + table + " on " + table + ".orc_key = oiu.orc_key inner join orc on " + table + ".orc_key = orc.orc_key where orc_status = 'C'";
								for (int i = 0; i < data.length; i++) {
									String field = (String) data[i].get("Access Policy.Policy Field Definition.Field Name");
									String value = (String) data[i].get("POF_FIELD_VALUE");
									query += " and " + field + " = " + value;
								}
								data = getOimData(query);
								if (data.length > 0) {
									bestObjectInstanceKey = Long.parseLong((String) data[0].get("Users-Object Instance For User.Key"));
								}
							}
							executeOimQuery("update oiu set pol_key = " + Long.toString(policyKey) + ", oiu_policy_revoke = " + Long.toString(revokeFlag) + ", oiu_policy_based = 1 where oiu_key = " + Long.toString(bestObjectInstanceKey));
						}
					}
				} finally {
					unlock(userKey);
				}
			}
			if (policyKey != 0L) {
				Map[] fieldData = getOimData("select distinct sdk_name from pof inner join sdk on pof.sdk_key = sdk.sdk_key where pol_key = " + Long.toString(policyKey) + " and pof.obj_key = " + Long.toString(objectKey));
				for (int i = 0; i < fieldData.length; i++) {
					String table = (String) fieldData[i].get("Structure Utility.Table Name");
					logger.debug("table = " + table);
					executeOimQuery("update " + table + " set pol_key = " + Long.toString(policyKey) + " where orc_key = " + Long.toString(processInstanceKey));
				}
				processChildFields(policyKey, processInstanceKey, objectKey);
				Map[] policies = getOimData("select pol_key from pol where pol_key in (select pol_key from pog where ugp_key in (select ugp_key from usg where usr_key = " + Long.toString(userKey) + ")) order by pol_priority desc");
				for (int i = 0; i < policies.length; i++) {
					policyKey = Long.parseLong((String) policies[i].get("Access Policies.Key"));
					logger.debug("policyKey = " + Long.toString(policyKey));
					processChildFields(policyKey, processInstanceKey, objectKey);
				}
			}
		} catch (Exception e) {
			OIMExceptionHandler.handleException(logger, e, getClass());
		}
		logger.debug("Exiting policyFlagChecker.processSingleInstance()");
	}
	
	/**
	 * Fixes up the policy profile for a user.
	 * 
	 * @param UserKey The key of the user to fix the policy profile for.
	 * @exception Exception
	 */
	public void fixPolicyProfile(long UserKey) throws Exception {
		logger.debug("Entering policyFlagChecker.fixPolicyProfile()");
		String query = "select distinct obj_name from pop inner join obj on pop.obj_key = obj.obj_key ";
		query += "where pol_key in (select pol_key from pog where ugp_key in (select ugp_key from usg ";
		query += "where usr_key = " + Long.toString(UserKey) + ")) and pop.obj_key not in (";
		query += "select obj_key from pop where pop_denial = '1' and pol_key in (";
		query += "select pol_key from pog where ugp_key in (select ugp_key from usg ";
		query += "where usr_key = " + Long.toString(UserKey) + ")))";
		String resourceList = "";
		Map[] resources = getOimData(query);
		for (int i = 0; i < resources.length; i++) {
			if (resourceList.length() > 0) {
				resourceList += ", ";
			}
			resourceList += (String) resources[i].get("Objects.Name");
			
		}
		executeOimQuery("update upp set upp_allow_list = '" + resourceList + "' where usr_key = " + Long.toString(UserKey));
		logger.debug("Exiting policyFlagChecker.fixPolicyProfile()");
	}
}